#ifndef __BLOCKINGQUEUE_H_
#define __BLOCKINGQUEUE_H_

#include <mutex>                // std::mutex, std::unique_lock
#include <condition_variable>    // std::condition_variable
#include <list>
#include <chrono>
namespace Util
{
	template<typename T>
	class BlockingQueue
	{
	public:
		BlockingQueue() : _mutex(), _condvar(), _queue()
		{

		}

		void put(const T& task)
		{
			{
				std::lock_guard<std::mutex> lock(_mutex);
				_queue.push_back(task);
			}
			_condvar.notify_one();
		}

		void take(T& task)
		{
			std::unique_lock<std::mutex> lock(_mutex);

			_condvar.wait(lock, [this]{return !_queue.empty();});

			task = _queue.front();
			_queue.pop_front();
		}

		bool take_for(T& task, int wait_sec)
		{
			std::unique_lock<std::mutex> lock(_mutex);
			if (_queue.empty())
				if (_condvar.wait_for(lock, std::chrono::seconds(wait_sec)) == std::cv_status::timeout)
					return false;

			task = _queue.front();
			_queue.pop_front();
			return true;
		}

		bool try_take(T& task)
		{
			std::unique_lock<std::mutex> lock(_mutex);
			if (_queue.empty())
				return false;

			task = _queue.front();
			_queue.pop_front();
			return true;
		}

		size_t size() const
		{
			std::lock_guard<std::mutex> lock(_mutex);
			return _queue.size();
		}

		void clear()
		{
			std::lock_guard<std::mutex> lock(_mutex);
			return _queue.clear();
		}


	private:
		std::mutex _mutex;
		std::condition_variable _condvar;
		std::list<T> _queue;
	};
}
#endif //__BLOCKINGQUEUE_H_